// solution copytight Christan Grant
#include <stdio.h>

#define INF 999
#define mynode (int)0 
#define TABLE_LENGTH 4

extern struct rtpkt {
  int sourceid;       /* id of sending router sending this pkt */
  int destid;         /* id of router to which pkt being sent 
                         (must be an immediate neighbor) */
  int mincost[TABLE_LENGTH];    /* min cost to node 0 ... 3 */
};

extern int TRACE;
extern int YES;
extern int NO;

int connectcosts0[TABLE_LENGTH];

struct distance_table 
{
  int costs[TABLE_LENGTH][TABLE_LENGTH];
} dt0;

void printdt0(struct distance_table *dtptr);
void linkhandler0(int linkid, int newcost);
extern void printFull(int dtptr[][TABLE_LENGTH]);
int costto0(int node);
extern void printdt(struct distance_table *dtptr, char* file_name);
extern char* table_filename;

/* read the network topology from a text file and initialize 
   the distance table
*/
int rtinit0() 
{
   FILE *fp = NULL;
   int val0,val1,val2,val3;
   int i=0;
   
   fp = fopen(table_filename,"r");
   
   if (fp == NULL){
      printf("Error opening file\n");
      return 1; 
   }
   
   for(i=0;i < TABLE_LENGTH; ++i){
      fscanf(fp, "%d %d %d %d",&val0, &val1, &val2, &val3);
      dt0.costs[i][0] = val0;
      dt0.costs[i][1] = val1;
      dt0.costs[i][2] = val2;
      dt0.costs[i][3] = val3;
      if(i == mynode){
         connectcosts0[0] = val0;
         connectcosts0[1] = val1;
         connectcosts0[2] = val2;
         connectcosts0[3] = val3;
      }
   }
   
   fclose( fp );
   
   struct rtpkt pkt2send;
   /* send routs to direct neighbors */
   for(i=0;i < TABLE_LENGTH; ++i){
      if((connectcosts0[i] != INF)&&(mynode != i)) {
         pkt2send.sourceid = mynode;
         pkt2send.destid = i;
         memcpy(pkt2send.mincost, dt0.costs[mynode], sizeof(pkt2send.mincost)); 
         tolayer2(pkt2send);
      }
   }
   
   //printf("ID: %d\n", mynode);
   //printFull(dt0.costs);
   return 0;
}

/*Called when node 0 receives a routing packet that was sent to it by one if 
   its directly connected neighbors. 
   The parameter *rcvdpkt is a pointer to the packet that was received. 
*/
void rtupdate0(rcvdpkt)
  struct rtpkt *rcvdpkt;
{
   int i=0;
   int source = rcvdpkt->sourceid;
   int dest = rcvdpkt->destid;
   /* Update all other rows in this matrix bc it is from the node*/
   for(i=0; i < TABLE_LENGTH; ++i){
      if(dt0.costs[source][i] > rcvdpkt->mincost[i]){
         dt0.costs[source][i] = rcvdpkt->mincost[i];
      }
   }


   for(i=0; i < TABLE_LENGTH; ++i){
      if(dt0.costs[i][source] > rcvdpkt->mincost[i]){
         dt0.costs[i][source] = rcvdpkt->mincost[i];
      }
   }

   
   /* Do for this row*/
   for(i=0; i < TABLE_LENGTH; ++i){
      int mincost = rcvdpkt->mincost[i];
      if(i == source){
         /* if this packet equals itself */
         continue;
      }
      else if( costto0(i) > (costto0(source) + mincost)){
         linkhandler0(i, costto0(source) + mincost);
         int j = i;
         struct rtpkt pkt2send;
         /* send routs to direct neighbors */
         for(i=0;i < TABLE_LENGTH; ++i){
            if((connectcosts0[i] != INF)&&(mynode != i)) {
               pkt2send.sourceid = mynode;
               pkt2send.destid = i;
               memcpy(pkt2send.mincost, dt0.costs[mynode], sizeof(pkt2send.mincost)); 
               tolayer2(pkt2send);
            }
         }
         i = j;
      }
   }
   if(TRACE >= 2){
      printFull(dt0.costs);
   }
}

void printdt0(dtptr)
  struct distance_table *dtptr;
  
{
  printf("                via     \n");
  printf("   D0 |    1     2    3 \n");
  printf("  ----|-----------------\n");
  printf("     1|  %3d   %3d   %3d\n",dtptr->costs[1][1],
	 dtptr->costs[1][2],dtptr->costs[1][3]);
  printf("dest 2|  %3d   %3d   %3d\n",dtptr->costs[2][1],
	 dtptr->costs[2][2],dtptr->costs[2][3]);
  printf("     3|  %3d   %3d   %3d\n",dtptr->costs[3][1],
	 dtptr->costs[3][2],dtptr->costs[3][3]);
}

void linkhandler0(linkid, newcost)   
  int linkid, newcost;

/* called when cost from 0 to linkid changes from current value to newcost*/
/* You can leave this routine empty if you're an undergrad. If you want */
/* to use this routine, you'll need to change the value of the LINKCHANGE */
/* constant definition in prog3.c from 0 to 1 */
   
{
   dt0.costs[mynode][linkid] = newcost;
   //printf("\n0--[%d][%d][%d]\n",mynode,linkid,newcost);
}

/*
   The distance table must be declared
   @returns the cost from this node to _node
*/
int costto0(int _node){
   return dt0.costs[mynode][_node];
}

extern void printFull(int dtptr[][TABLE_LENGTH])
{
   int i=0,j=0;
   for(i=0;i<TABLE_LENGTH;++i){
      for(j=0;j<TABLE_LENGTH;++j){
         printf("%4d",dtptr[i][j]);
      }
      printf("\n");
   }
   printf("\n");
}

/*
   prints specified distance table to disk
*/
extern void printdt(dtptr,file_name)
  struct distance_table *dtptr;
  char* file_name;
{
   FILE* fp = fopen(file_name, "w");
   
   fprintf(fp,"%d %d %d %d\n",dtptr->costs[0][0],dtptr->costs[0][1],dtptr->costs[0][2],dtptr->costs[0][3]);
   fprintf(fp,"%d %d %d %d\n",dtptr->costs[1][0],dtptr->costs[1][1],dtptr->costs[1][2],dtptr->costs[1][3]);
   fprintf(fp,"%d %d %d %d\n",dtptr->costs[2][0],dtptr->costs[2][1],dtptr->costs[2][2],dtptr->costs[2][3]);
   fprintf(fp,"%d %d %d %d\n",dtptr->costs[3][0],dtptr->costs[3][1],dtptr->costs[3][2],dtptr->costs[3][3]);
   
   fclose(fp);
}
